iT邦幫忙

2017 iT 邦幫忙鐵人賽
DAY 14
1
自我挑戰組

Android初學筆記系列 第 14

Day 14 - 使用RecyclerView(2)

  • 分享至 

  • xImage
  •  

延續昨天建立的RecyclerView,我們實作幾個功能來發掘它厲害的地方。

自訂項目佈局

實務上,列表要顯示的項目經常不只一行字,還會放更多圖文訊息甚至可互動的元件,我們就放一個Button到我們項目中。

修改list_item

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="72dp"
    android:gravity="center_vertical"
    android:orientation="horizontal">

    <TextView
        android:id="@+id/txtItem"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1" />

    <Button
        android:id="@+id/btnRemove"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="刪除"/>

</LinearLayout>

第6行我們把android:orientation改成horizontal讓文字和按鈕水平並排。

可以特別注意的是12行我們把TextView加上android:layout_weight="1"這個屬性,這是LinearLayout專用的屬性,表示元件的空間比重。

此處TextView的weight是1而Button沒有設置所以是0,效果為TextView能獲得Button之外的全部空間,如圖所示藍色框都是TextView的可用空間,Button就只有符合本身大小的空間。
http://ithelp.ithome.com.tw/upload/images/20161229/20103849Kt4FKFf97N.png
對weight不清楚的話可以嘗試把Button的weight也改為1,兩個元件就會變一樣大,改成2的話就會是2:1的大小。

在MyAdapter的ViewHolder中連結Button,而Button的點擊事件待會一起介紹
http://ithelp.ithome.com.tw/upload/images/20161229/20103849twXXdW9zZ4.png


點擊事件

RecyclerView並沒有內建的點擊Listener可以用,但沒關係,它的用法是在ViewHolder裡寫點擊事件,其實寫起來很簡單而且彈性,可以為一列中的每個元件個別設置

class ViewHolder extends RecyclerView.ViewHolder{
        // 宣告元件
        private TextView txtItem;
        private Button btnRemove;

        ViewHolder(View itemView) {
            super(itemView);
            txtItem = (TextView) itemView.findViewById(R.id.txtItem);
            btnRemove = (Button) itemView.findViewById(R.id.btnRemove);

            // 點擊項目時
            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    Toast.makeText(view.getContext(),
                            "click " +getAdapterPosition(),Toast.LENGTH_SHORT).show();
                }
            });

            // 點擊項目中的Button時
            btnRemove.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    // 按下Button要做的事
                }
            });
        }
    }

itemView表示整個項目,我們令點擊時出現Toast。這邊也可以為元件個別設置點擊事件,當按下btnRemove時要做的事可以跟按下itemView不同。

起初RecyclerView沒有內建點擊Listener令我相當錯愕,覺得這元件什麼都沒有,一貧如洗。不過瞭解以後其實這樣超方便的對吧!比用ListView和onItemSelectListener彈性很多,很容易擴充元件且程式碼也很簡單。


點擊回饋

沒錯,RecyclerView也沒有內建的點擊視覺回饋,目前點下去看起來完全沒反應,要有視覺回饋的話只要加一行程式到list_item裡就可以了。

android:background="?android:attr/selectableItemBackground"

http://ithelp.ithome.com.tw/upload/images/20161229/20103849H8Mspd2ZOf.png

執行結果


增減項目

RecyclerView在增減項目時有內建動畫效果,個人覺得這點RecyclerView大優於ListView,雖然也不是什麼驚天地泣鬼神的動畫,但我是設計白癡,覺得內建這樣的效果已經很好了。

先在activity_main加上一個按鈕用來新增項目

<Button
    android:id="@+id/btnAdd"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="新增"/>

新增和刪除項目的程式則寫在MyAdapter

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {

    // 未更動的程式省略..

    // 新增項目
    public void addItem(String text) {
        // 為了示範效果,固定新增在位置3。若要新增在最前面就把3改成0
        mData.add(3,text);
        notifyItemInserted(3);
    }

    // 刪除項目
    public void removeItem(int position){
        mData.remove(position);
        notifyItemRemoved(position);
    }
}

將ViewHolder中的btnRemove加上刪除功能

btnRemove.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    // 移除項目,getAdapterPosition為點擊的項目位置
                    removeItem(getAdapterPosition());
                }
            });

MainActivity加上新增按鈕的部分

btnAdd.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // 新增一個項目
                adapter.addItem("New Item");
            }
        });

執行結果


列表樣式(LayoutManager)

RecyclerView可以用LayoutManager來改變樣式,除了一般的List樣式,也可以用成Grid的方格,只要一行程式就可以了哦!

我們可以從這兩種選用一種樣式

// Linear型態,第二個參數控制垂直或水平,第三個參數為是否reverse順序
recycler_view.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
// Grid型態,第二個參數控制一列顯示幾項
recycler_view.setLayoutManager(new GridLayoutManager(this, 2));

(其實還有第三種StaggeredGridLayout(瀑布型),但在此例看不出效果,教材準備失敗真是抱歉/images/emoticon/emoticon16.gif)

格線也可以選用要垂直的或水平的

// 第二個參數VERTICAL或HORIZONTAL控制垂直或水平
recycler_view.addItemDecoration(
    new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));

而且不管你用哪種樣式,增減項目的動畫都能運作哦!用Grid樣式的效果如下:


總結來說,RecyclerView一開始建立需要多一點點步驟,但往後的延展性非常強,可以輕易的自訂項目佈局和整體樣式,而且效能更好,Reddit討論串也一面倒的建議入門的夥伴使用RecyclerView作為列表元件。

希望這兩天的文章能幫助大家順利的使用它,明天再補充關於點擊事件進一步的用法。

2018 update:

點擊事件請優先考慮interface而非明天的EventBus,因為EventBus在專案擴大時不好維護。建立interface方式:

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {

    // 1. 建立interface,命名為OnItemClickHandler,並在裡面寫好我們要發生的事件
    interface OnItemClickHandler {
        // 提供onItemClick方法作為點擊事件,括號內為接受的參數
        void onItemClick(String text);
        // 提供onItemRemove做為移除項目的事件
        void onItemRemove(int position, String text);
    }

    private List<String> mData;
    // 2. 宣告interface
    private OnItemClickHandler mClickHandler;

    // 3. 修改Constructor
    MyAdapter(List<String> data, OnItemClickHandler clickHandler) {
        mData = data;
        mClickHandler = clickHandler;
    }
    
    class ViewHolder extends RecyclerView.ViewHolder{
        ...

        ViewHolder(final View itemView) {
            super(itemView);
            ...

            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    String msg = mData.get(getAdapterPosition());
                    // 4. 呼叫interface的method
                    mClickHandler.onItemClick(msg);
                }
            });

            btnRemove.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    int position = getAdapterPosition();
                    String text = mData.get(position);
                    // 4. 呼叫interface的method
                    mClickHandler.onItemRemove(position, text);
                }
            });
        }
    }

    ...
}

修改MainActivity,接收MyAdapter的OnItemClickHandler

// 1. implements要接收的interface
public class MainActivity extends AppCompatActivity implements MyAdapter.OnItemClickHandler {

    ...
    
    private MyAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ...
        
        // 2. 修改Adapter建立方式,this指此Activity,因為我們已經implement所以此Activity本身就是一個OnItemClickHandler
        adapter = new MyAdapter(mData, this);
        recycler_view.setAdapter(adapter);
        
        ...
    }

    // 3. 點擊事件
    @Override
    public void onItemClick(String text) {
        // text即為點擊的內容,可在此顯示Toast或其他處理
    }
    
    // 3. 移除事件
    @Override
    public void onItemRemove(int position, String text) {
        // do something...
    }
}

在要接收點擊事件的Activity/Fragment中implement OnItemClickHandler,之後Android Studio會有紅燈泡提醒你要建立method,即OnItemClickHandler中宣告的那兩個,就可以在method中寫點擊後要做的事了。


上一篇
Day 13 - 使用RecyclerView(1)
下一篇
Day 15 - EventBus
系列文
Android初學筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言